package com.xci.platform.auth.core;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpUtil;
import com.xci.platform.auth.entity.*;
import com.xci.platform.auth.service.*;
import com.xci.platform.core.BoolMessage;
import com.xci.platform.core.Constant;
import com.xci.platform.core.Simple;
import com.xci.platform.core.TreeNode;
import com.xci.platform.helper.*;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.beanutils.BeanComparator;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Component
public class AuthHelper {

    @Resource
    private ParamService paramService;

    /**
     * 用户服务
     */
    @Resource
    private UserService userService;
    /**
     * 在线用户服务
     */
    @Resource
    private OnlineUserService onlineUserService;

    /**
     * 模块服务
     */
    @Resource
    private ModuleService moduleService;

    @Resource
    protected AuthHelper authHelper;

    /**
     * 角色服务
     */
    @Resource
    private RoleService roleService;

    /**
     * 日志服务
     */
    @Resource
    private LogService logService;

    @Resource
    private DicDetailsService dicDetailsService;

    /**
     * 内容字典服务
     */
    @Resource
    private ContentService contentService;

    /// <summary>
    /// 密码强度监测正则表达式
    /// </summary>
    public static final Pattern PasswordStrongPattern = Pattern.compile("(?=.*[0-9])(?=.*[a-zA-Z])(?=([\\x21-\\x7e]+)[^a-zA-Z0-9]).{6,}");

    /**
     * 根据字典编码获取字典明细列表
     *
     * @param dicCode 字典编码
     */
    public List<Simple> getDetailsSimple(@NotNull String dicCode) {
        return dicDetailsService.querySimpleByDicCode(dicCode);
    }

    /**
     * 根据参数编码获取参数值
     *
     * @param code 参数编码
     * @return 返回参数编码对应的参数值,如果找不到指定的参数则返回空字符串
     */
    public String getParamValue(@NotNull String code) {
        return getParamValue(code, Constant.Empty);
    }

    /**
     * 根据参数编码获取参数值
     *
     * @param code         参数编码
     * @param defaultValue 找不到参数时返回的默认值
     * @return 返回参数编码对应的参数值
     */
    public String getParamValue(@NotNull String code, String defaultValue) {
        ParamEntity entity = paramService.queryByCode(code);
        if (entity == null) return defaultValue;
        return entity.getValue();
    }

    /**
     * 根据内容字典编码获取内容值
     *
     * @param code 内容字典编码
     * @return 返回内容字典编码对应的内容值
     */
    public String getContentValue(@NotNull String code) {
        ContentEntity entity = contentService.queryByCode(code);
        if (entity == null) return Constant.Empty;
        return entity.getContentValue();
    }

    /**
     * 获取用户简单信息列表
     */
    public List<Simple> getUserSimpleList() {
        return userService.querySimple(null);
    }

    /**
     * 当前操作用户
     */
    public AuthUser currentUser() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (WebHelper.isApp(request)) {
            String userId = String.valueOf(request.getAttribute(Constant.ApiTokenKey));
            if (StringHelper.isNotBlank(userId)) {
                UserEntity entity = userService.queryById(userId);
                return buildAuthUser(entity, request);
            }
        } else {
            Object user = request.getSession().getAttribute(Constant.UserSessionKey);
            return (AuthUser) user;
        }
        return null;
    }

    /**
     * 是否有指定模块编码的权限
     *
     * @param moduleCode 功能编码
     * @return 如果授权返回true
     */
    public boolean isAuthorize(String moduleCode) {
        if (StringHelper.isBlank(moduleCode)) {
            return false;
        }
        AuthUser authUser = currentUser();
        if (authUser.getIsAdmin()) {
            return true;
        }
        Map<String, ModuleEntity> permissionMap = moduleService.queryCacheMapByUserId(authUser.getUserId());
        String[] codes = StringHelper.splitToArray(moduleCode);//必须包含全部编码才可以
        for (String code : codes) {
            boolean has = permissionMap.containsKey(moduleCode);
            if (!has) return false; //任何一个为false,则直接返回false;
        }
        return true;
    }

    /**
     * 设置用户登录cookie
     */
    public void setUserCookie(UserEntity entity) throws UnsupportedEncodingException {
        String cookieValue = entity.getId();
        Cookie cookie = new Cookie(Constant.UserCookieKey, URLEncoder.encode(cookieValue, Constant.UTF8));
        cookie.setPath("/");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(60 * 60 * 24 * 7);//7天
        CookieHelper.setCookie(cookie);
    }

    /**
     * 检查登录状态,并自动登录
     */
    public boolean checkAndAutoLogin(HttpServletRequest request) {
        Object user = request.getSession().getAttribute(Constant.UserSessionKey);
        if (user != null) {
            return true;
        }
        long startTime = CoreHelper.startWatch();
        String userCookie = CookieHelper.getCookie(Constant.UserCookieKey);
        if (userCookie != null) {
            UserEntity entity = userService.queryById(userCookie);
            if (entity != null) {
                BoolMessage sresult = onLoginSuccess(entity, startTime, request);
                if (sresult.isSuccess()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 用户登录成功函数
     */
    public BoolMessage onLoginSuccess(UserEntity entity, long startTime, HttpServletRequest request) {
        OnlineUser onlineUser = onlineUserService.get(entity.getId());
        if (authHelper.getParamValue("SystemAllowRepeatLogin","1").equals("0") && onlineUser != null) {
            return BoolMessage.fail(StringHelper.format("用户在 {} 在IP:{} 登录,系统不允许重复登录!", DateHelper.formatDateTime(onlineUser.getLoginTime()), onlineUser.getIpAddress()));
        }
        AuthUser authUserModel = buildAuthUser(entity, request);
        request.getSession().setAttribute(Constant.UserSessionKey, authUserModel);

        //更新登录状态
        userService.updateLoginStatus(entity);
        //激活在线用户
        onlineUserService.active(new OnlineUser(authUserModel));
        //添加登录审计日志
        LogEntity logEntity = buildAuditInfo(request, "登录成功", CoreHelper.stopWatch(startTime));
        logService.insert(logEntity);
        return BoolMessage.True;
    }

    /**
     * 获取当前用户角色列表
     */
    public List<RoleEntity> getRoleList(AuthUser authUser) {
        if (authUser.getIsAdmin()) {
            return new ArrayList<>();
        }
        return roleService.queryListByUserId(authUser.getUserId());
    }

    /**
     * 获取当前用户角色字符串
     */
    public String getRoleString(AuthUser authUser) {
        if (authUser.getIsAdmin()) {
            return "超级管理员";
        }
        List<RoleEntity> roleList = roleService.queryListByUserId(authUser.getUserId());
        return roleList.stream().map(RoleEntity::getName).collect(Collectors.joining(","));
    }

    /**
     * 获取当前用户模块树节点
     *
     * @param authUser 当前用户
     * @param params   查询参数
     */
    public List<TreeNode> getModuleTreeList(AuthUser authUser, Map<String, Object> params) {
        List<ModuleEntity> list;
        if (authUser.getIsAdmin()) {
            params.put("status", 1);
            list = moduleService.query(params);
        } else {
            list = moduleService.queryCacheByUserId(authUser.getUserId());
            list.sort(new BeanComparator<>("path"));
        }
        Object isMenuObj = params.get("isMenu");//是否只查询菜单
        if (ObjectHelper.isNotBlank(isMenuObj)) {
            list = list.stream().filter(p -> p.getIsMenu().equals(isMenuObj)).collect(Collectors.toList());
        }
        return moduleService.convertToNodeList(list);
    }

    /**
     * 构建审计日志对象
     *
     * @param request 请求对象
     */
    public static LogEntity buildAuditInfo(HttpServletRequest request, String message, long costTime) {
        LogEntity entity = buildLogInfo(request, null);
        entity.setOperateType(LogOperateType.Audit.getId());
        if (WebHelper.isApp(request)) {
            entity.setModule("App-Login");
        } else {
            entity.setModule("SpringMvc-Login");
        }
        entity.setCostTime(costTime);
        entity.setResult(1);
        entity.setMessage(message);
        return entity;
    }

    /**
     * 构建日志对象
     *
     * @param request  请求对象
     * @param logModel 日志模型
     */
    public static LogEntity buildLogInfo(HttpServletRequest request, LogModel logModel) {
        LogEntity entity = new LogEntity();
        entity.setId(SecureUtil.simpleUUID());
        if (logModel != null) {
            entity.setOperateType(logModel.getOperateType().getId());
            entity.setModule(logModel.getModule());
            entity.setCostTime(logModel.getCostTime());
            entity.setResult(logModel.isResult() ? 1 : 0);
            entity.setMessage(logModel.getMessage());
        }
        if (request != null) {
            if (request.getHeader("User-Agent") != null) {
                UserAgent agent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
                String browserInfo = StrUtil.format("{} {} {}"
                        , agent.getOperatingSystem().getName()
                        , agent.getBrowser().getName()
                        , agent.getBrowserVersion().getMajorVersion());
                entity.setBrowser(browserInfo);
            }
            entity.setIpAddress(HttpUtil.getClientIP(request));
            entity.setUrl(request.getRequestURI());
        }
        return entity;
    }

    /**
     * 根据异常对象构建日志消息
     *
     * @param request 请求对象
     * @param ex      异常对象
     */
    public static LogEntity buildExceptionLogInfo(HttpServletRequest request, Exception ex) {
        LogEntity entity = buildLogInfo(request, null);
        entity.setResult(0);
        entity.setOperateType(LogOperateType.Exception.getId());
        entity.setMessage(ex.getMessage());
        StringWriter stringWriter = new StringWriter();
        ex.printStackTrace(new PrintWriter(stringWriter));
        entity.setDetails(stringWriter.toString());
        return entity;
    }

    /**
     * 生成登录用户对象
     */
    public static AuthUser buildAuthUser(UserEntity userEntity, HttpServletRequest request) {
        AuthUser currentUser = new AuthUser();
        currentUser.setUserId(userEntity.getId());
        currentUser.setUserName(userEntity.getName());
        currentUser.setAccount(userEntity.getAccount());
        currentUser.setDepartmentId(userEntity.getDepartmentId());
        currentUser.setDepartmentName(userEntity.getDepartmentName());
        currentUser.setIpAddress(HttpUtil.getClientIP(request));
        currentUser.setBrowser(WebHelper.getBrowserInfo(request));
        currentUser.setIsAdmin(userEntity.getIsAdmin() == 1);
        currentUser.setLoginTime(new Date());
        currentUser.setUserEntity(userEntity);
        return currentUser;
    }

    /**
     * 设置实体当前操作用户字段值
     *
     * @param entity      实体对象
     * @param currentUser 当前用户对象
     */
    public static void setOperateUser(Object entity, AuthUser currentUser) {
        Method method = ReflectUtil.getMethod(entity.getClass(), "setOperateDateTime");
        if (method != null) {
            try {
                method.invoke(entity, new Date());
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }

        if (currentUser != null) {
            method = ReflectUtil.getMethod(entity.getClass(), "setOperateUserId");
            if (method != null) {
                try {
                    method.invoke(entity, currentUser.getUserId());
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
            method = ReflectUtil.getMethod(entity.getClass(), "setOperateUserName");
            if (method != null) {
                try {
                    method.invoke(entity, currentUser.getUserName());
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 标记新建记录
     *
     * @param map 模型对象
     */
    public static void createMark(ModelMap map) {
        map.put(Constant.CreateMark, "1");
    }

    /**
     * 是否新建记录
     */
    public static boolean IsCreate(HttpServletRequest request) {
        return request.getParameter(Constant.CreateMark).equals("1");
    }

    /**
     * 获取站点Url信息
     */
    public static String getSiteUrl() {
        return WebHelper.getSiteUrl();
    }


    /**
     * 格式化日期(默认格式yyyy-MM-dd)
     *
     * @param date 指定的时间
     * @return 返回日期格式化后的字符串
     */
    public static String formatDate(Date date) {
        return DateHelper.formatDate(date);
    }

    /**
     * 格式化日期字符串(默认格式 yyyy-MM-dd HH:mm)
     *
     * @param date 指定的时间
     * @return 返回日期格式化后的字符串
     */
    public static String formatDateHasMinute(Date date) {
        return DateHelper.formatDateHasMinute(date);
    }

    /**
     * 格式化日期(默认格式 yyyy-MM-dd HH:mm:ss)
     *
     * @param date 指定的时间
     * @return 返回日期格式化后的字符串
     */
    public static String formatDateTime(Date date) {
        return DateHelper.formatDateTime(date);
    }

    /**
     * 获取格式化的时间字符串(默认格式 HH:mm:ss)
     *
     * @param date 指定的时间
     * @return 返回日期格式化后的字符串
     */
    public static String formatTime(Date date) {
        return DateHelper.formatTime(date);
    }

    /**
     * 格式化日期
     *
     * @param date   指定的时间
     * @param format 格式字符串
     * @return 返回日期格式化后的字符串
     */
    public static String formatDate(Date date, String format) {
        return DateHelper.formatDate(date,format);
    }


    /**
     * 以 conjunction 为分隔符将多个对象转换为字符串
     *
     * @see ArrayUtil#join(Object, CharSequence)
     *
     * @param conjunction 分隔符
     * @param objs 数组
     * @return 连接后的字符串
     */
    public static String join(CharSequence conjunction, Object... objs){
        return ArrayUtil.join(objs, conjunction);
    }
    public static boolean contains(String source, String sub) {
        return source.toLowerCase().contains(sub.toLowerCase());
    }

    /**
     * 字符串是否为空白 空白的定义如下： <br>
     * 1、为null <br>
     * 2、为不可见字符（如空格）<br>
     * 3、""<br>
     *
     * @param str 被检测的字符串
     * @return 是否为空
     */
    public static boolean isBlank(CharSequence str) {
        return StrUtil.isBlank(str);
    }

    /**
     * 字符串是否为非空白 空白的定义如下： <br>
     * 1、不为null <br>
     * 2、不为不可见字符（如空格）<br>
     * 3、不为""<br>
     *
     * @param str 被检测的字符串
     * @return 是否为非空
     */
    public static boolean isNotBlank(CharSequence str) {
        return !isBlank(str);
    }

}
